feat: Add Xiaomi channel support and update related model ratios#4590
feat: Add Xiaomi channel support and update related model ratios#4590HynoR wants to merge 5 commits intoQuantumNous:mainfrom
Conversation
WalkthroughAdds Xiaomi MiMo channel: new channel/API constants and mappings, frontend options, vendor/model entries, stream support, a Xiaomi relay adaptor with TTS request/response handling, and wiring into adaptor selection. ChangesXiaomi Channel Integration
sequenceDiagram
participant Client
participant XiaomiAdaptor
participant XiaomiAPI as Xiaomi API Server
participant TTSHandler
Client->>XiaomiAdaptor: Send audio-speech (TTS) request
XiaomiAdaptor->>XiaomiAdaptor: Normalize format, default voice, build payload
XiaomiAdaptor->>XiaomiAPI: POST /v1/chat/completions (TTS payload)
XiaomiAPI-->>TTSHandler: HTTP JSON response with base64 audio
TTSHandler->>TTSHandler: Unmarshal, validate, base64-decode audio
TTSHandler->>Client: Return decoded audio with appropriate Content-Type (200)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@model/pricing_default.go`:
- Line 22: defaultVendorIcons is missing an entry for the new vendor string
"小米", so newly created vendors get no icon; add a mapping in the
defaultVendorIcons map that maps the vendor name "小米" to the existing icon key
(e.g., "mimo") used elsewhere. Locate the defaultVendorIcons variable and add an
entry like `"小米": "mimo"` so the new Xiaomi vendor resolves to the proper icon.
In `@relay/channel/xiaomi/tts.go`:
- Around line 65-67: In handleTTSResponse, guard resp and resp.Body before
deferring Close: check if resp == nil or resp.Body == nil and return an
appropriate *types.NewAPIError instead of deferring; only call defer
resp.Body.Close() after verifying both are non-nil. Update the error return path
to produce the same typed API error used elsewhere in this function so callers
receive a consistent error shape.
In `@setting/ratio_setting/model_ratio.go`:
- Around line 252-256: The model ratio map is missing explicit entries for
Xiaomi TTS variants so lookups fall back to the default; add entries for
"mimo-v2-tts" and "mimo-v2.5-tts" to the same ratio values as their non-TTS
counterparts (e.g., mirror "mimo-v2-omni"/"mimo-v2-pro" behavior — use 0.5 for
v2-tts and 0.5 for v2.5-tts or align with the existing ratios scheme used in the
map) by inserting those keys into the same map literal in model_ratio.go so TTS
billing uses the correct ratio instead of the default.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3465aac7-d9e1-49ed-a0af-17632c911a43
📒 Files selected for processing (12)
common/api_type.goconstant/api_type.goconstant/channel.gomodel/pricing_default.gorelay/channel/xiaomi/adaptor.gorelay/channel/xiaomi/constants.gorelay/channel/xiaomi/tts.gorelay/common/relay_info.gorelay/relay_adaptor.gosetting/ratio_setting/model_ratio.goweb/classic/src/constants/channel.constants.jsweb/default/src/features/channels/constants.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@relay/channel/xiaomi/adaptor.go`:
- Around line 36-41: ConvertOpenAIRequest currently returns the incoming request
unchanged, bypassing the base adaptor's logic that clears StreamOptions for
non-OpenAI/Azure channels; update Xiaomi's ConvertOpenAIRequest to delegate to
the base implementation (call a.Adaptor.ConvertOpenAIRequest(c, info, request))
and return its result so the base logic (which nils request.StreamOptions when
info.ChannelType is not constant.ChannelTypeOpenAI or constant.ChannelTypeAzure)
is applied for ChannelTypeXiaomi; alternatively ensure the method explicitly
clears request.StreamOptions for non-OpenAI/Azure before returning, but prefer
delegating to the base adaptor to keep behavior consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 43d3560a-757e-493a-9288-11705cb1ac55
📒 Files selected for processing (3)
relay/channel/xiaomi/adaptor.gorelay/channel/xiaomi/tts.gosetting/ratio_setting/model_ratio.go
✅ Files skipped from review due to trivial changes (1)
- setting/ratio_setting/model_ratio.go
🚧 Files skipped from review as they are similar to previous changes (1)
- relay/channel/xiaomi/tts.go
| func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.GeneralOpenAIRequest) (any, error) { | ||
| if request == nil { | ||
| return nil, fmt.Errorf("request is nil") | ||
| } | ||
| return request, nil | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Inspect openai.Adaptor.ConvertOpenAIRequest for StreamOptions injection
# and any other request mutations that Xiaomi's override would silently bypass.
# 1. Find the full implementation of ConvertOpenAIRequest in the openai adaptor
ast-grep --pattern 'func (a *Adaptor) ConvertOpenAIRequest($_, $_, $_) ($_, $_) {
$$$
}'
# 2. Confirm which channels are in streamSupportedChannels and whether Xiaomi is present
rg -n "streamSupportedChannels\|ChannelTypeXiaomi" relay/common/relay_info.go -A 3 -B 1
# 3. Find where StreamOptions is set / injected into the request body
rg -n "StreamOptions\|stream_options\|StreamOption" relay/channel/openai/adaptor.go -A 3 -B 3Repository: QuantumNous/new-api
Length of output: 34700
🏁 Script executed:
# Check the base OpenAI adaptor's StreamOptions handling logic
rg -n "StreamOptions" relay/channel/openai/adaptor.go -B 3 -A 3
# Verify if Xiaomi is in streamSupportedChannels
rg -n "streamSupportedChannels" relay/common/relay_info.go -A 30
# Check Xiaomi's channel type constant
rg -n "ChannelTypeXiaomi\|Xiaomi" relay/ -t go | head -20Repository: QuantumNous/new-api
Length of output: 2983
🏁 Script executed:
# Get full context around lines 76-80 to understand which function sets StreamOptions
sed -n '60,90p' relay/channel/openai/adaptor.go
# Check if there are multiple ConvertOpenAIRequest calls or if one delegates to another
rg -n "ConvertOpenAIRequest" relay/channel/xiaomi/adaptor.go -B 5 -A 10
# Verify Xiaomi's base class/embedding structure
head -50 relay/channel/xiaomi/adaptor.goRepository: QuantumNous/new-api
Length of output: 3316
🏁 Script executed:
# Check if there are tests or documentation indicating what Xiaomi expects
find relay/channel/xiaomi -type f -name "*.go" -o -name "*.md" -o -name "*.txt" | xargs ls -la
# Look for usage patterns or comments about StreamOptions in Xiaomi context
rg -n "StreamOptions\|stream_options\|include_usage" relay/channel/xiaomi/ -t go
# Check if Xiaomi needs to clear StreamOptions like the base does
rg -n "ChannelTypeXiaomi" relay/channel/ -t go -B 2 -A 2Repository: QuantumNous/new-api
Length of output: 304
🏁 Script executed:
# View the exact base adaptor logic again to confirm the StreamOptions clearing behavior
sed -n '229,240p' relay/channel/openai/adaptor.go
# Check if there's any conditional logic that would preserve StreamOptions for Xiaomi
rg -n "SupportStreamOptions\|info.ChannelType.*Xiaomi" relay/channel/openai/adaptor.go -A 3 -B 3Repository: QuantumNous/new-api
Length of output: 579
ConvertOpenAIRequest bypasses required base adaptor logic for StreamOptions handling.
The override returns the request unchanged without delegating to a.Adaptor.ConvertOpenAIRequest. The base OpenAI adaptor includes logic (line 234) to clear StreamOptions from requests for all non-OpenAI/Azure channels:
if info.ChannelType != constant.ChannelTypeOpenAI && info.ChannelType != constant.ChannelTypeAzure {
request.StreamOptions = nil
}Since Xiaomi's ChannelType is ChannelTypeXiaomi, the base adaptor would clear StreamOptions to prevent it from being sent upstream. By returning the request unchanged, this override silently bypasses that clearing logic, leaving StreamOptions in the request body when it should be removed.
The simplest fix is to delegate to the base adaptor:
🔧 Proposed fix
func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.GeneralOpenAIRequest) (any, error) {
- if request == nil {
- return nil, fmt.Errorf("request is nil")
- }
- return request, nil
+ return a.Adaptor.ConvertOpenAIRequest(c, info, request)
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@relay/channel/xiaomi/adaptor.go` around lines 36 - 41, ConvertOpenAIRequest
currently returns the incoming request unchanged, bypassing the base adaptor's
logic that clears StreamOptions for non-OpenAI/Azure channels; update Xiaomi's
ConvertOpenAIRequest to delegate to the base implementation (call
a.Adaptor.ConvertOpenAIRequest(c, info, request)) and return its result so the
base logic (which nils request.StreamOptions when info.ChannelType is not
constant.ChannelTypeOpenAI or constant.ChannelTypeAzure) is applied for
ChannelTypeXiaomi; alternatively ensure the method explicitly clears
request.StreamOptions for non-OpenAI/Azure before returning, but prefer
delegating to the base adaptor to keep behavior consistent.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
web/classic/src/helpers/render.jsx (1)
408-414: ⚡ Quick winAdd a dev-time warning when icon resolution falls back to Avatar.
Right now unresolved icon names silently degrade to first-letter Avatar. A non-prod warning makes mapping typos easy to catch during review/testing.
Proposed patch
if ( !IconComponent || (typeof IconComponent !== 'function' && typeof IconComponent !== 'object') ) { + if (process.env.NODE_ENV !== 'production') { + console.warn(`[getLobeHubIcon] unresolved icon: ${iconName}`); + } const firstLetter = String(iconName).charAt(0).toUpperCase(); return <Avatar size='extra-extra-small'>{firstLetter}</Avatar>; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/classic/src/helpers/render.jsx` around lines 408 - 414, When IconComponent is missing or invalid and you fall back to Avatar, add a dev-only warning so typos are visible during testing: inside the existing conditional that checks IconComponent (the block that currently computes firstLetter and returns <Avatar>), emit a console.warn (guarded by process.env.NODE_ENV !== 'production') that includes the unresolved iconName and a short hint like "icon not resolved, falling back to Avatar" so reviewers can spot mapping mistakes; keep the existing Avatar fallback logic (firstLetter) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/classic/src/helpers/render.jsx`:
- Around line 367-370: Replace the early-return null when an unknown channel
type is encountered so the UI renders a neutral fallback icon instead of blank
space: in the block using channelTypeIconMap and iconName (the lines with const
iconName = channelTypeIconMap[channelType]; if (!iconName) return null;), set
iconName to a defined fallback (e.g., 'generic' or 'unknown') when the lookup
fails and proceed to render the icon as usual; ensure the fallback key exists in
the channelTypeIconMap (or add it) so render logic that consumes iconName
continues to work for new/unsupported channel types.
---
Nitpick comments:
In `@web/classic/src/helpers/render.jsx`:
- Around line 408-414: When IconComponent is missing or invalid and you fall
back to Avatar, add a dev-only warning so typos are visible during testing:
inside the existing conditional that checks IconComponent (the block that
currently computes firstLetter and returns <Avatar>), emit a console.warn
(guarded by process.env.NODE_ENV !== 'production') that includes the unresolved
iconName and a short hint like "icon not resolved, falling back to Avatar" so
reviewers can spot mapping mistakes; keep the existing Avatar fallback logic
(firstLetter) unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d993a534-d3c9-452c-9f25-10723fcadeb9
📒 Files selected for processing (4)
model/pricing_default.gorelay/channel/xiaomi/tts.goweb/classic/src/helpers/render.jsxweb/default/src/features/channels/lib/channel-utils.ts
✅ Files skipped from review due to trivial changes (2)
- model/pricing_default.go
- web/default/src/features/channels/lib/channel-utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- relay/channel/xiaomi/tts.go
| const iconName = channelTypeIconMap[channelType]; | ||
| if (!iconName) { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
Use a fallback icon instead of returning null for unknown channel types.
Line 369 returns null, which creates blank icons when a new/unsupported channelType shows up. A neutral fallback keeps list rendering stable.
Proposed patch
export function getChannelIcon(channelType) {
const iconSize = 14;
const iconName = channelTypeIconMap[channelType];
- if (!iconName) {
- return null;
- }
- return getLobeHubIcon(iconName, iconSize);
+ return getLobeHubIcon(iconName || '', iconSize);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/classic/src/helpers/render.jsx` around lines 367 - 370, Replace the
early-return null when an unknown channel type is encountered so the UI renders
a neutral fallback icon instead of blank space: in the block using
channelTypeIconMap and iconName (the lines with const iconName =
channelTypeIconMap[channelType]; if (!iconName) return null;), set iconName to a
defined fallback (e.g., 'generic' or 'unknown') when the lookup fails and
proceed to render the icon as usual; ensure the fallback key exists in the
channelTypeIconMap (or add it) so render logic that consumes iconName continues
to work for new/unsupported channel types.
Important
支持小米渠道添加(含token plan 版本)
📝 变更描述 / Description
支持小米推理模型,并支持 tts 模型(openai tts 原生调用或者 comletion 官方兼容)
🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。📸 运行证明 / Proof of Work
(请在此粘贴截图、关键日志或测试报告,以证明变更生效)


Summary by CodeRabbit